/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.commons.net.examples.ftp;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.*;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;
import org.apache.commons.net.util.TrustManagerUtils;

import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

/**
 * This is an example program demonstrating how to use the FTPClient class.
 * This program connects to an FTP server and retrieves the specified
 * file.  If the -s flag is used, it stores the local file at the FTP server.
 * Just so you can see what's happening, all reply strings are printed.
 * If the -b flag is used, a binary transfer is assumed (default is ASCII).
 * See below for further options.
 */
public final class FTPClientExample
{

	public static final String USAGE =
			"Expected Parameters: [options] <hostname> <username> <password> [<remote file> [<local file>]]\n" +
					"\nDefault behavior is to download a file and use ASCII transfer mode.\n" +
					"\t-a - use local active mode (default is local passive)\n" +
					"\t-A - anonymous login (omit username and password parameters)\n" +
					"\t-b - use binary transfer mode\n" +
					"\t-c cmd - issue arbitrary command (remote is used as a parameter if provided) \n" +
					"\t-d - list directory details using MLSD (remote is used as the pathname if provided)\n" +
					"\t-e - use EPSV with IPv4 (default false)\n" +
					"\t-E - encoding to use for control channel\n" +
					"\t-f - issue FEAT command (remote and local files are ignored)\n" +
					"\t-h - list hidden files (applies to -l and -n only)\n" +
					"\t-i - issue SIZE command for a file\n" +
					"\t-k secs - use keep-alive timer (setControlKeepAliveTimeout)\n" +
					"\t-l - list files using LIST (remote is used as the pathname if provided)\n" +
					"\t     Files are listed twice: first in raw mode, then as the formatted parsed data.\n" +
					"\t     N.B. if the wrong server-type is used, output may be lost. Use -U or -S as necessary.\n" +
					"\t-L - use lenient future dates (server dates may be up to 1 day into future)\n" +
					"\t-m - list file details using MDTM (remote is used as the pathname if provided)\n" +
					"\t-n - list file names using NLST (remote is used as the pathname if provided)\n" +
					"\t-p true|false|protocol[,true|false] - use FTPSClient with the specified protocol and/or isImplicit setting\n" +
					"\t-s - store file on server (upload)\n" +
					"\t-S - systemType set server system type (e.g. UNIX VMS WINDOWS)\n" +
					"\t-t - list file details using MLST (remote is used as the pathname if provided)\n" +
					"\t-U - save unparseable responses\n" +
					"\t-w msec - wait time for keep-alive reply (setControlKeepAliveReplyTimeout)\n" +
					"\t-T  all|valid|none - use one of the built-in TrustManager implementations (none = JVM default)\n" +
					"\t-y format - set default date format string\n" +
					"\t-Y format - set recent date format string\n" +
					"\t-Z timezone - set the server timezone for parsing LIST responses\n" +
					"\t-z timezone - set the timezone for displaying MDTM, LIST, MLSD, MLST responses\n" +
					"\t-PrH server[:port] - HTTP Proxy host and optional port[80] \n" +
					"\t-PrU user - HTTP Proxy server username\n" +
					"\t-PrP password - HTTP Proxy server password\n" +
					"\t-# - add hash display during transfers\n";

	public static void main(final String[] args) throws UnknownHostException
	{
		boolean storeFile = false, binaryTransfer = false, error = false, listFiles = false, listNames = false, hidden = false;
		boolean localActive = false, useEpsvWithIPv4 = false, feat = false, printHash = false;
		boolean mlst = false, mlsd = false, mdtm = false, saveUnparseable = false;
		boolean size = false;
		boolean lenient = false;
		long keepAliveTimeout = -1;
		int controlKeepAliveReplyTimeout = -1;
		int minParams = 5; // listings require 3 params
		String protocol = null; // SSL protocol
		String doCommand = null;
		String trustmgr = "none";
		String proxyHost = null;
		int proxyPort = 80;
		String proxyUser = null;
		String proxyPassword = null;
		String username = null;
		String password = null;
		String encoding = null;
		String serverTimeZoneId = null;
		String displayTimeZoneId = null;
		String serverType = null;
		String defaultDateFormat = null;
		String recentDateFormat = null;


		int base = 0;
		for (base = 0; base < args.length; base++)
		{
			if (args[base].equals("-s")) {
				storeFile = true;
			}
			else if (args[base].equals("-a")) {
				localActive = true;
			}
			else if (args[base].equals("-A")) {
				username = "anonymous";
				password = System.getProperty("user.name")+"@"+InetAddress.getLocalHost().getHostName();
			}
			else if (args[base].equals("-b")) {
				binaryTransfer = true;
			}
			else if (args[base].equals("-c")) {
				doCommand = args[++base];
				minParams = 3;
			}
			else if (args[base].equals("-d")) {
				mlsd = true;
				minParams = 3;
			}
			else if (args[base].equals("-e")) {
				useEpsvWithIPv4 = true;
			}
			else if (args[base].equals("-E")) {
				encoding = args[++base];
			}
			else if (args[base].equals("-f")) {
				feat = true;
				minParams = 3;
			}
			else if (args[base].equals("-h")) {
				hidden = true;
			}
			else if (args[base].equals("-i")) {
				size = true;
				minParams = 3;
			}
			else if (args[base].equals("-k")) {
				keepAliveTimeout = Long.parseLong(args[++base]);
			}
			else if (args[base].equals("-l")) {
				listFiles = true;
				minParams = 3;
			}
			else if (args[base].equals("-m")) {
				mdtm = true;
				minParams = 3;
			}
			else if (args[base].equals("-L")) {
				lenient = true;
			}
			else if (args[base].equals("-n")) {
				listNames = true;
				minParams = 3;
			}
			else if (args[base].equals("-p")) {
				protocol = args[++base];
			}
			else if (args[base].equals("-S")) {
				serverType = args[++base];
			}
			else if (args[base].equals("-t")) {
				mlst = true;
				minParams = 3;
			}
			else if (args[base].equals("-U")) {
				saveUnparseable = true;
			}
			else if (args[base].equals("-w")) {
				controlKeepAliveReplyTimeout = Integer.parseInt(args[++base]);
			}
			else if (args[base].equals("-T")) {
				trustmgr = args[++base];
			}
			else if (args[base].equals("-y")) {
				defaultDateFormat = args[++base];
			}
			else if (args[base].equals("-Y")) {
				recentDateFormat = args[++base];
			}
			else if (args[base].equals("-Z")) {
				serverTimeZoneId = args[++base];
			}
			else if (args[base].equals("-z")) {
				displayTimeZoneId = args[++base];
			}
			else if (args[base].equals("-PrH")) {
				proxyHost = args[++base];
				final String parts[] = proxyHost.split(":");
				if (parts.length == 2){
					proxyHost=parts[0];
					proxyPort=Integer.parseInt(parts[1]);
				}
			}
			else if (args[base].equals("-PrU")) {
				proxyUser = args[++base];
			}
			else if (args[base].equals("-PrP")) {
				proxyPassword = args[++base];
			}
			else if (args[base].equals("-#")) {
				printHash = true;
			}
			else {
				break;
			}
		}

		final int remain = args.length - base;
		if (username != null) {
			minParams -= 2;
		}
//		if (remain < minParams) // server, user, pass, remote, local [protocol]
//		{
//			if (args.length > 0) {
//				System.err.println("Actual Parameters: " + Arrays.toString(args));
//			}
//			System.err.println(USAGE);
//			System.exit(1);
//		}
		username = "test";
		String server = "127.0.0.1";//args[base++];
		int port = 222;
//		final String parts[] = server.split(":");
//		if (parts.length == 2){
//			server=parts[0];
//			port=Integer.parseInt(parts[1]);
//		}
//		if (username == null) {
//			username = args[base++];
//			password = args[base++];
//		}
		password = "123456";
		String remote = "/1.xlsx";
		/**
		String remote = null;
		if (args.length - base > 0) {
			remote = args[base++];
		}*/

		String local = "e:/湖南移动信控开机接入端到端割接步骤V2.0.xlsx";
//		if (args.length - base > 0) {
//			local = args[base++];
//		}

		final FTPClient ftp;
		if (protocol == null ) {
			if(proxyHost !=null) {
				System.out.println("Using HTTP proxy server: " + proxyHost);
				ftp = new FTPHTTPClient(proxyHost, proxyPort, proxyUser, proxyPassword);
			}
			else {
				ftp = new FTPClient();
			}
		} else {
			FTPSClient ftps;
			if (protocol.equals("true")) {
				ftps = new FTPSClient(true);
			} else if (protocol.equals("false")) {
				ftps = new FTPSClient(false);
			} else {
				final String prot[] = protocol.split(",");
				if (prot.length == 1) { // Just protocol
					ftps = new FTPSClient(protocol);
				} else { // protocol,true|false
					ftps = new FTPSClient(prot[0], Boolean.parseBoolean(prot[1]));
				}
			}
			ftp = ftps;
			if ("all".equals(trustmgr)) {
				ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());
			} else if ("valid".equals(trustmgr)) {
				ftps.setTrustManager(TrustManagerUtils.getValidateServerCertificateTrustManager());
			} else if ("none".equals(trustmgr)) {
				ftps.setTrustManager(null);
			}
		}

		if (printHash) {
			ftp.setCopyStreamListener(createListener());
		}
		if (keepAliveTimeout >= 0) {
			ftp.setControlKeepAliveTimeout(keepAliveTimeout);
		}
		if (controlKeepAliveReplyTimeout >= 0) {
			ftp.setControlKeepAliveReplyTimeout(controlKeepAliveReplyTimeout);
		}
		if (encoding != null) {
			ftp.setControlEncoding(encoding);
		}
		ftp.setListHiddenFiles(hidden);

		// suppress login details
		ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true));

		final FTPClientConfig config;
		if (serverType != null) {
			config = new FTPClientConfig(serverType);
		} else {
			config = new FTPClientConfig();
		}
		config.setUnparseableEntries(saveUnparseable);
		if (defaultDateFormat != null) {
			config.setDefaultDateFormatStr(defaultDateFormat);
		}
		if (recentDateFormat != null) {
			config.setRecentDateFormatStr(recentDateFormat);
		}
		ftp.configure(config);

		try
		{
			int reply;
			if (port > 0) {
				ftp.connect(server, port);
			} else {
				ftp.connect(server);
			}
			System.out.println("Connected to " + server + " on " + (port>0 ? port : ftp.getDefaultPort()));

			// After connection attempt, you should check the reply code to verify
			// success.
			reply = ftp.getReplyCode();

			if (!FTPReply.isPositiveCompletion(reply))
			{
				ftp.disconnect();
				System.err.println("FTP server refused connection.");
				System.exit(1);
			}
		}
		catch (final IOException e)
		{
			if (ftp.isConnected())
			{
				try
				{
					ftp.disconnect();
				}
				catch (final IOException f)
				{
					// do nothing
				}
			}
			System.err.println("Could not connect to server.");
			e.printStackTrace();
			System.exit(1);
		}

		__main:
		try
		{
			if (!ftp.login(username, password))
			{
				ftp.logout();
				error = true;
				break __main;
			}

			System.out.println("Remote system is " + ftp.getSystemType());
			binaryTransfer = true;
			if (binaryTransfer) {
				ftp.setFileType(FTP.BINARY_FILE_TYPE);
			} else {
				// in theory this should not be necessary as servers should default to ASCII
				// but they don't all do so - see NET-500
				ftp.setFileType(FTP.ASCII_FILE_TYPE);
			}

			// Use passive mode as default because most of us are
			// behind firewalls these days.
			if (localActive) {
				ftp.enterLocalActiveMode();
			} else {
				ftp.enterLocalPassiveMode();
			}

			ftp.setUseEPSVwithIPv4(useEpsvWithIPv4);
			storeFile = true;
			if (storeFile)
			{
				InputStream input = null;
				try   {
					input = new FileInputStream(local);
					ftp.storeFile(remote, input);
				}catch (Exception e){
					e.printStackTrace();
				}
				finally {
					if(input != null){
						input.close();
					}
				}

				if (keepAliveTimeout > 0) {
					showCslStats(ftp);
				}
			}
			// Allow multiple list types for single invocation
			else if (listFiles || mlsd || mdtm || mlst || listNames || size)
			{
				if (mlsd) {
					for (final FTPFile f : ftp.mlistDir(remote)) {
						System.out.println(f.getRawListing());
						System.out.println(f.toFormattedString(displayTimeZoneId));
					}
				}
				if (mdtm) {
					final FTPFile f = ftp.mdtmFile(remote);
					if (f != null) {
						System.out.println(f.getRawListing());
						System.out.println(f.toFormattedString(displayTimeZoneId));
					} else {
						System.out.println("File not found");
					}
				}
				if (mlst) {
					final FTPFile f = ftp.mlistFile(remote);
					if (f != null){
						System.out.println(f.toFormattedString(displayTimeZoneId));
					}
				}
				if (listNames) {
					for (final String s : ftp.listNames(remote)) {
						System.out.println(s);
					}
				}
				if (size) {
					System.out.println("Size="+ftp.getSize(remote));
				}
				// Do this last because it changes the client
				if (listFiles) {
					if (lenient || serverTimeZoneId != null) {
						config.setLenientFutureDates(lenient);
						if (serverTimeZoneId != null) {
							config.setServerTimeZoneId(serverTimeZoneId);
						}
						ftp.configure(config );
					}

					for (final FTPFile f : ftp.listFiles(remote)) {
						System.out.println(f.getRawListing());
						System.out.println(f.toFormattedString(displayTimeZoneId));
					}
				}
			}
			else if (feat)
			{
				// boolean feature check
				if (remote != null) { // See if the command is present
					if (ftp.hasFeature(remote)) {
						System.out.println("Has feature: "+remote);
					} else {
						if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
							System.out.println("FEAT "+remote+" was not detected");
						} else {
							System.out.println("Command failed: "+ftp.getReplyString());
						}
					}

					// Strings feature check
					final String []features = ftp.featureValues(remote);
					if (features != null) {
						for(final String f : features) {
							System.out.println("FEAT "+remote+"="+f+".");
						}
					} else {
						if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
							System.out.println("FEAT "+remote+" is not present");
						} else {
							System.out.println("Command failed: "+ftp.getReplyString());
						}
					}
				} else {
					if (ftp.features()) {
//                        Command listener has already printed the output
					} else {
						System.out.println("Failed: "+ftp.getReplyString());
					}
				}
			}
			else if (doCommand != null)
			{
				if (ftp.doCommand(doCommand, remote)) {
//                  Command listener has already printed the output
//                    for(String s : ftp.getReplyStrings()) {
//                        System.out.println(s);
//                    }
				} else {
					System.out.println("Failed: "+ftp.getReplyString());
				}
			}
			else
			{
				OutputStream output = null;
				try  {
					output = new FileOutputStream(local);
					ftp.retrieveFile(remote, output);
				}
				catch (Exception e){
					e.printStackTrace();
				}
				finally {
					if(output != null)
						output.close();
				}

				if (keepAliveTimeout > 0) {
					showCslStats(ftp);
				}
			}

			ftp.noop(); // check that control connection is working OK

			ftp.logout();
		}
		catch (final FTPConnectionClosedException e)
		{
			error = true;
			System.err.println("Server closed connection.");
			e.printStackTrace();
		}
		catch (final IOException e)
		{
			error = true;
			e.printStackTrace();
		}
		finally
		{
			if (ftp.isConnected())
			{
				try
				{
					ftp.disconnect();
				}
				catch (final IOException f)
				{
					// do nothing
				}
			}
		}

		System.exit(error ? 1 : 0);
	} // end main

	private static void showCslStats(final FTPClient ftp) {
		@SuppressWarnings("deprecation") // debug code
		final
		int []stats = ftp.getCslDebug();
		System.out.println("CslDebug="+Arrays.toString(stats));

	}

	private static CopyStreamListener createListener(){
		return new CopyStreamListener(){
			private long megsTotal = 0;

			@Override
			public void bytesTransferred(final CopyStreamEvent event) {
				bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
			}

			@Override
			public void bytesTransferred(final long totalBytesTransferred,
										 final int bytesTransferred, final long streamSize) {
				final long megs = totalBytesTransferred / 1000000;
				for (long l = megsTotal; l < megs; l++) {
					System.err.print("#");
				}
				megsTotal = megs;
			}
		};
	}
}
